#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
THN DATA ABSTRACTION LAYER version 1

Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

This library is dual-licensed.
  + For commercial software, please obtain a commercial license from THN Solutions LLC.

  + For free software, this library is licensed under GPL version 3. A summary of GPLv3 is
    listed below. You should also find a copy of GPLv3 as file License_GPLv3.txt included
	with the source-code files.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 3 as published by
    the Free Software Foundation  of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion

#region Using directives
using System;
using System.Collections;
using Thn.Data.Query;
using Thn.Data.Mapping;
#endregion

namespace Thn.Data.Database
{
    /// <summary>
    /// An abstract base for database classes
    /// </summary>
    public abstract class AbstractDatabase : IDatabase
    {
        #region Mapper
        /// <summary>
        /// Data mapper
        /// </summary>
        protected IDataMapper mMapper = new ReflectionDataMapper();
        /// <summary>
        /// Gets/Sets Mapper
        /// </summary>
        public IDataMapper Mapper
        {
            get { return mMapper; }
            set { mMapper = value; }
        }
        #endregion		
		
        #region I/O
        #region Initialize
        /// <summary>
        /// Prepare connection to database
        /// </summary>
        /// <param name="parameters">connection string</param>
        public abstract void Initialize(string parameters);        
        #endregion

        #region Refresh
        /// <summary>
        /// Refresh/load data, if neccessary
        /// </summary>
        public abstract void Refresh();
        #endregion

        #region Flush
        /// <summary>
        /// Persist changes to database, if still buffered
        /// </summary>
        public abstract void Flush();
        #endregion

        #region Close
        /// <summary>
        /// Close connection to the underlying database, if neccessary
        /// </summary>
        public abstract void Close();
        #endregion
        #endregion

        #region Transaction
        #region Begin Transaction
        /// <summary>
        /// Start a new transaction.
        /// </summary>
        /// <returns>The transaction context specific to database</returns>
        public abstract object BeginTransaction();
        #endregion

        #region Commit Transaction
        /// <summary>
        /// Execute task(s) contained in this transaction and persist changes to database
        /// </summary>
        /// <param name="transactionContext">The transaction context specific to database</param>
        public abstract void CommitTransaction(object transactionContext);
        #endregion

        #region Cancel Transaction
        /// <summary>
        /// Abandon changes made within this transaction
        /// </summary>
        /// <param name="transactionContext">The transaction context specific to database</param>
        public abstract void CancelTransaction(object transactionContext);
        #endregion
        #endregion

        #region Register
        /// <summary>
        /// Contains a collection of objects already registered
        /// </summary>
        Hashtable mRegisteredObjectTypes = new Hashtable();

        #region Auto register
        bool mAutoRegister = true;
        /// <summary>
        /// If true, object type will be registered automatically the first time it is processed
        /// </summary>
        public virtual bool AutoRegister
        {
            get { return mAutoRegister; }
            set { mAutoRegister = value; }
        }
        #endregion

        #region Is registered
        /// <summary>
        /// Checks whether an object type has been registered.
        /// If AutoRegister is true, all object type will always be registered upon checking the first time
        /// </summary>
        protected bool IsRegistered(Type ObjectType)
        {
            bool result = false;

            if (mRegisteredObjectTypes.ContainsKey(ObjectType)) result = true;
            else if (mAutoRegister)
            {
                Register(ObjectType);
                result = true;
            }

            return result;
        }
        #endregion

        #region Register
        /// <summary>
        /// Subcribe a type of data object to this database
        /// </summary>
        public virtual void Register(Type objectType)
        {
            if (!mRegisteredObjectTypes.ContainsKey(objectType))
            {
                mRegisteredObjectTypes.Add(objectType, objectType);
            }
        }
        #endregion

        #region Unregister
        /// <summary>
        /// Unsubcribe a type of data object from this database
        /// </summary>
        public virtual void Unregister(Type objectType)
        {
            if (mRegisteredObjectTypes.ContainsKey(objectType))
            {
                mRegisteredObjectTypes.Remove(objectType);
            }            
        }
        #endregion

        #region Register Types
        /// <summary>
        /// Gets all data types registered to this database
        /// </summary>
        public Type[] RegisteredTypes
        {
            get
            {
                Type[] results = new Type[mRegisteredObjectTypes.Count];

                int i = 0;

                foreach (object obj in mRegisteredObjectTypes.Values)
                {
                    results[i] = (Type)obj;
                    i++;
                }

                return results;
            }
        }
        #endregion
        #endregion

        #region Structuring
        #region Create structure for 1 object type
        /// <summary>
        /// Generate database structure specific to a single object type
        /// </summary>
        /// <param name="objectType">type of data object to generate</param>
        /// <param name="keepOldData">try to retain the existing data</param>
        public abstract void CreateStructure(Type objectType, bool keepOldData);
        #endregion

        #region Create all structures
        /// <summary>
        /// Generate database structures (table, stored procedure, etc.) of all registered object types
        /// </summary>
        /// <param name="keepOldData">try to retain the existing data</param>
        public abstract void CreateStructure(bool keepOldData);
        #endregion
        #endregion

        #region Execute
        /// <summary>
        /// Proccess a query command
        /// </summary>
        /// <param name="command">The command to process</param>
        public abstract ExecuteResult Execute(QueryBase command);
        #endregion

        #region Begin
        /// <summary>
        /// Gets a new transaction scope
        /// </summary>
        public TransactionQuery Begin
        {
            get
            {
                TransactionQuery qry = new TransactionQuery();
                qry.Processor = this;
                return qry;
            }
        }
        #endregion

        #region Insert
        #region Insert object(s)
        /// <summary>
        /// Add new object(s) into database
        /// </summary>
        public abstract void InsertObjects(params object[] values);
        #endregion

        #region Insert collection
        /// <summary>
        /// Add a collection of objects to database
        /// </summary>
        public abstract void InsertObjects(IList values);
        #endregion

        /// <summary>
        /// Gets a new Insert query
        /// </summary>
        public virtual InsertQuery Insert(params object[] values)
        {
            InsertQuery qry = new InsertQuery(values);
            qry.Processor = this;
            return qry;
        }

        /// <summary>
        /// Gets a new Insert query
        /// </summary>
        public virtual InsertQuery Insert(IList values)
        {
            InsertQuery qry = new InsertQuery(values);
            qry.Processor = this;
            return qry;
        }
        #endregion

        #region Select
        /// <summary>
        /// Get object(s) matching the condition
        /// </summary>
        /// <param name="objectType">Type of object to query against</param>
        /// <param name="condition">Query criteria</param>
        /// <returns>A collection is always returned. If there are no object matches the condition, the collection is empty</returns>
        public abstract IList SelectObjects(Type objectType, Condition condition);

        /// <summary>
        /// Gets a new select query
        /// </summary>
        public virtual SelectQuery Select
        {
            get
            {
                SelectQuery qry = new SelectQuery();
                qry.Processor = this;
                return qry;
            }
        }
        #endregion

        #region Update
        #region Update object(s)
        /// <summary>
        /// Update object(s) into database
        /// </summary>
        public abstract void UpdateObjects(params object[] values);
        #endregion

        #region Update collection
        /// <summary>
        /// Update a collection of objects to database
        /// </summary>
        public abstract void UpdateObjects(IList values);
        #endregion

        /// <summary>
        /// Gets a new update query
        /// </summary>
        public virtual UpdateQuery Update(params object[] values)
        {
            UpdateQuery qry = new UpdateQuery(values);
            qry.Processor = this;
            return qry;
        }

        /// <summary>
        /// Gets a new update query
        /// </summary>
        public virtual UpdateQuery Update(IList values)
        {
            UpdateQuery qry = new UpdateQuery(values);
            qry.Processor = this;
            return qry;
        }
        #endregion

        #region Delete
        /// <summary>
        /// Delete object(s) from database using their identifiers
        /// </summary>
        /// <param name="values">objects to delete</param>
        public abstract void DeleteObjects(params object[] values);

        /// <summary>
        /// Delete object(s) from database using their identifiers
        /// </summary>
        /// <param name="values">objects to delete</param>
        public abstract void DeleteObjects(IList values);

        /// <summary>
        /// Gets a new Delete query
        /// </summary>
        public virtual DeleteQuery Delete(params object[] values)
        {
            DeleteQuery qry = new DeleteQuery(values);
            qry.Processor = this;
            return qry;
        }

        /// <summary>
        /// Gets a new Delete query
        /// </summary>
        public virtual DeleteQuery Delete(IList values)
        {
            DeleteQuery qry = new DeleteQuery(values);
            qry.Processor = this;
            return qry;
        }

        /// <summary>
        /// Gets a new Delete query
        /// </summary>
        public virtual DeleteQuery Delete()
        {
            DeleteQuery qry = new DeleteQuery();
            qry.Processor = this;
            return qry;
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public AbstractDatabase()
        {
        }
        #endregion
    }
}
